Overview
Nouns DAO will integrate with Octant v2 through the Lido Yield Skimming Strategy — an ERC-4626 vault that captures yield from wstETH exchange rate appreciation (~3-5% APY from ETH staking rewards).
| Component | Purpose | Capital |
|---|---|---|
| Lido Yield Skimming Strategy | Generate yield for public goods funding | wstETH held by treasury |
Architecture Note: The strategy IS the ERC-4626 vault. No MultistrategyVault wrapper is needed since only one strategy is approved by the DAO. This simplifies deployment, reduces gas costs, and eliminates unnecessary complexity.
Prerequisites (Pending Items)
The following items must be resolved before executing the DAO proposal:
| Item | Status | Owner | Notes |
|---|---|---|---|
| Dragon Funding Pool address | ⏳ Pending | Nouns DAO | Strategy donation recipient (PaymentSplitter) |
| Keeper Bot address | ⏳ Pending | Nouns DAO | Strategy keeper for harvesting |
| Emergency Shutdown Admin address | ⏳ Pending | Nouns DAO | Can shutdown strategy and perform emergency withdrawals |
| wstETH deposit amount | ⏳ Pending | Nouns DAO | Amount of wstETH to deposit (treasury holds ~1,725 wstETH) |
⚠️ Action Required: Update this document with actual addresses before submitting the proposal.
Open Questions for Nouns DAO
The following questions need to be answered by the Nouns DAO team before finalizing the proposal:
| Question | Context | Status |
|---|---|---|
| wstETH deposit amount | How much wstETH does Nouns DAO want to deposit into the strategy? Treasury holds ~1,725 wstETH. | ⏳ Pending |
| Proposal UI format | The deployment requires 4 transactions. In the Nouns DAO UI (nouns.wtf/vote), will this be: Option A: 4 separate calldata entries (one per transaction), or Option B: 1 batched calldata containing all 4 transactions? |
⏳ Pending |
| Keeper operator | Who will operate the Keeper bot? Options: Nouns DAO team, Golem Foundation, third-party service | ⏳ Pending |
| Emergency Admin setup | Who should be the Emergency Admin? Recommend a trusted multisig (2-of-3 or 3-of-5) for rapid response | ⏳ Pending |
Note: The script can generate output for both proposal formats (individual transactions or batched). Confirm the preferred format before generating final calldata.
Verified On-Chain Addresses
| Entity | Address | Network |
|---|---|---|
| Nouns DAO Treasury (Executor/Timelock) | 0xb1a32FC9F9D8b2cf86C068Cae13108809547ef71 |
Ethereum |
| Nouns DAO Governor Proxy | 0x6f3E6272A167e8AcCb32072d08E0957F9c79223d |
Ethereum |
| Nouns NFT Token | 0x9C8fF314C9Bc7F6e59A9d9225Fb22946427eDC03 |
Ethereum |
| wstETH | 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0 |
Ethereum |
| PaymentSplitter Factory | 0x5711765E0756B45224fc1FdA1B41ab344682bBcb |
Ethereum |
Contracts To Be Deployed
The following contracts must be deployed to mainnet before the DAO proposal:
| Contract | Purpose | Status |
|---|---|---|
| YieldSkimmingTokenizedStrategy | Base implementation for yield-skimming strategies | ⏳ Pending deployment |
| LidoStrategyFactory | Factory for deploying LidoStrategy instances via CREATE2 | ⏳ Pending deployment |
Note: These contracts will be deployed by the Golem Foundation team. Addresses will be updated here once deployed.
Lido Yield Skimming Strategy
The LidoStrategy is an ERC-4626 vault (via Yearn's TokenizedStrategy) that tracks wstETH → stETH exchange rate appreciation. Treasury deposits wstETH directly into the strategy.
Underlying Yield Source
wstETH (Wrapped Staked ETH) — Lido's non-rebasing wrapper around stETH.
- Mechanism: Exchange rate increases as staking rewards accrue to stETH
- Rate Source:
wstETH.stEthPerToken()(manipulation-resistant, oracle-free) - Yield: ~3-5% APY from ETH validator staking rewards
- Risk: ETH validator slashing can reduce exchange rate
Yield Capture Mechanism
Role Assignments
| Role | Assigned To | Description |
|---|---|---|
| Management | Nouns DAO Treasury (0xb1a3...ef71) |
Administrative role (set keeper, set emergency admin, shutdown) |
| Keeper | Dedicated Bot/EOA | REQUIRED: Authorized to call report() to harvest yields without governance votes |
| Emergency Admin | Nouns DAO Treasury (0xb1a3...ef71) |
Can shutdown the strategy and perform emergency withdrawals |
Critical: The Keeper should be a dedicated EOA or bot, NOT the Treasury. Assigning Keeper to Treasury would require a governance vote for every harvest (7+ day minimum voting cycle), creating severe operational bottlenecks.
Yield Distribution
| Destination | Allocation |
|---|---|
| Dragon Funding Pool | 100% |
Strategy Configuration Parameters
_enableBurning
The _enableBurning parameter controls whether the strategy can burn Dragon shares to cover losses.
| Value | Behavior |
|---|---|
false |
Dragon shares are never burned. If the strategy incurs a loss (e.g., wstETH exchange rate decreases due to slashing), the loss is socialized among all depositors. Dragon operations are blocked if the vault cannot cover user debt. |
true |
Dragon shares can be burned to cover losses before affecting user principal. Provides additional protection for depositors but reduces Dragon's accumulated yield. |
Recommendation: Set to false for Nouns DAO. Lido slashing events are extremely rare, and disabling burning simplifies accounting while ensuring Dragon receives all earned yield.
Changing the Donation Address (Dragon Router)
The donation address can be changed after deployment, but it requires a two-step process with a mandatory 14-day cooldown. This protects depositors by giving them time to exit if they disagree with the change.
Step 1: Initiate Change (Management only)
Target: [STRATEGY_ADDRESS]
Function: setDragonRouter(address)
Selector: 0x59bd9f07
Value: 0
Parameters:
_dragonRouter: [NEW_PAYMENT_SPLITTER_ADDRESS]
Access: Management only (requires governance vote)
Effect: Starts 14-day cooldown, emits PendingDragonRouterChange event
Step 2: Wait 14 Days (Cooldown Period)
During this period:
- Users can see the pending change via
pendingDragonRouter()anddragonRouterChangeTimestamp() - Users who disagree can withdraw their funds
- Management can cancel via
cancelDragonRouterChange()if needed
Step 3: Finalize Change (Permissionless)
Target: [STRATEGY_ADDRESS]
Function: finalizeDragonRouterChange()
Selector: 0x0e98ea4e
Value: 0
Parameters: (none)
Access: Anyone (permissionless after cooldown)
Effect: Applies the new dragon router address
Cancel Pending Change (Optional)
Target: [STRATEGY_ADDRESS]
Function: cancelDragonRouterChange()
Selector: 0x940be647
Value: 0
Parameters: (none)
Access: Management only
Effect: Cancels pending change, resets cooldown
Timeline: A dragon router change requires ~21 days total:
- ~7 days for governance proposal (propose → vote → queue → execute)
- 14 days mandatory cooldown
- Then anyone can finalize
PaymentSplitter (Dragon Funding Pool)
The PaymentSplitter is deployed via the existing factory and receives yield from the strategy.
Recommended Configuration
| Payee | Allocation | Purpose |
|---|---|---|
| Nouns Ecosystem Grants | 100% | Public goods funding |
Note: Multiple payees with different allocations can be configured if desired (e.g., 80% grants, 20% operations).
Nouns DAO Governance
Architecture
Nouns DAO uses a Governor + Timelock pattern with NFT-based voting:
| Component | Address / Value |
|---|---|
| Governor Proxy | 0x6f3E6272A167e8AcCb32072d08E0957F9c79223d |
| Executor (Timelock) | 0xb1a32FC9F9D8b2cf86C068Cae13108809547ef71 |
| Voting Token | Nouns NFT (0x9C8fF314...DC03) |
Proposal Lifecycle
A Nouns DAO proposal goes through several stages before execution:
Execution Call Chain
Critical: From the target contract's perspective, msg.sender is the Executor/Timelock address (0xb1a3...), NOT the Governor. This is why all roles are assigned to the Executor address.
Governance Parameters
See current governance parameters on nouns.wtf/vote.
Voting Platforms
| Platform | Type | Use Case |
|---|---|---|
| nouns.wtf/vote | On-chain | Treasury transactions, contract interactions |
| Discourse | Forum | Discussion, temperature checks |
Execution Playbook
The full deployment can be executed in a single DAO proposal with 4 batched transactions:
- Deploy PaymentSplitter via Factory
- Deploy LidoStrategy via Factory
- Approve wstETH to Strategy
- Deposit wstETH into Strategy
Prerequisites: Before submitting the proposal:
- YieldSkimmingTokenizedStrategy must be deployed to mainnet
- LidoStrategyFactory must be deployed to mainnet
- Strategy factory uses CREATE2, allowing address precomputation
Step 1: Create Proposal
1.1 — Navigate to Nouns DAO
Open nouns.wtf/vote
1.2 — Connect Wallet
Connect a wallet with sufficient voting power (own or delegated Nouns).
1.3 — Click "Submit Proposal"
Navigate to the proposals section and initiate a new proposal.
1.4 — Fill Proposal Details
| Field | Value |
|---|---|
| Title | Deploy Octant Lido Yield Strategy for wstETH |
| Description | See Proposal Template below |
1.5 — Add Transaction 1: Deploy PaymentSplitter
| Field | Value |
|---|---|
| Target Contract | 0x5711765E0756B45224fc1FdA1B41ab344682bBcb (PaymentSplitter Factory) |
| Function | createPaymentSplitter(address[],string[],uint256[]) |
payees |
[[GRANT_RECIPIENT_ADDRESS]] |
payeeNames |
["NounsGrants"] |
shares |
[100] |
1.6 — Add Transaction 2: Deploy Strategy
| Field | Value |
|---|---|
| Target Contract | [LIDO_STRATEGY_FACTORY_ADDRESS] |
| Function | createStrategy(string,address,address,address,address,bool,address) |
_name |
NounsLidoStrategy |
_management |
0xb1a32FC9F9D8b2cf86C068Cae13108809547ef71 |
_keeper |
[KEEPER_BOT_ADDRESS] (dedicated bot/EOA) |
_emergencyAdmin |
[EMERGENCY_ADMIN_ADDRESS] (dedicated multisig/EOA, NOT Treasury) |
_donationAddress |
[PAYMENT_SPLITTER_ADDRESS] (from Tx 1) |
_enableBurning |
false |
_tokenizedStrategyAddress |
[YIELD_SKIMMING_TOKENIZED_STRATEGY_ADDRESS] |
1.7 — Add Transaction 3: Approve wstETH
| Field | Value |
|---|---|
| Target Contract | 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0 (wstETH) |
| Function | approve(address spender, uint256 amount) |
spender |
[STRATEGY_ADDRESS] (from Tx 2) |
amount |
[DEPOSIT_AMOUNT] (e.g., 1000e18 for 1000 wstETH) |
1.8 — Add Transaction 4: Deposit wstETH
| Field | Value |
|---|---|
| Target Contract | [STRATEGY_ADDRESS] (from Tx 2) |
| Function | deposit(uint256 assets, address receiver) |
assets |
[DEPOSIT_AMOUNT] (e.g., 1000e18 for 1000 wstETH) |
receiver |
0xb1a32FC9F9D8b2cf86C068Cae13108809547ef71 |
1.9 — Submit Proposal
Review all details and submit the proposal.
Gas Profile
| Component | Gas Cost |
|---|---|
| PaymentSplitter Deployment | ~50k |
| LidoStrategy Deployment | ~3-4M |
| wstETH Approve | ~50k |
| wstETH Deposit | ~200k |
| Total (batched) | ~4M |
| Limit | Value | Notes |
|---|---|---|
| Per-Transaction Gas Limit | 16,777,216 (2²⁴) | Hard cap via EIP-7825 (Fusaka upgrade) |
| Block Gas Limit | 30M | Maximum per block |
| Headroom | >75% | Proposal well within limits |
Note: The Ethereum "Fusaka" upgrade introduced a per-transaction gas cap of 16,777,216 gas (2²⁴) via EIP-7825. This is a hard protocol limit distinct from the block gas limit. Our ~4M total is safely within this cap.
Step 2: Vote
- Share the proposal link on Discourse for discussion
- Noun holders vote during the voting period (~3 days)
- Proposal passes if quorum is met and majority votes "For"
Step 3: Queue
Once voting passes, the proposal enters the timelock queue (~2 days).
Step 4: Execute
After the timelock delay:
- Return to the proposal page on nouns.wtf
- Click "Execute" (available after timelock)
- Sign the execution transaction
- Verify on Etherscan that all transactions succeeded
Step 5: Verify Deployment
After execution, verify:
- ☐ PaymentSplitter deployed with correct payees
- ☐ Strategy deployed with correct donation address (PaymentSplitter)
- ☐ Treasury received strategy shares
- ☐ wstETH deposited into strategy
Proposal Template
## Summary
This proposal deploys the Octant Lido Yield Skimming Strategy and deposits
[AMOUNT] wstETH from Nouns DAO treasury as part of the Octant v2 pilot.
## Background
Octant v2 enables DAOs to optimize treasury yield while funding public goods.
The Lido strategy captures yield from wstETH exchange rate appreciation
(~3-5% annually from ETH staking rewards) and routes it to designated recipients.
## Transactions (4 total)
1. **Deploy PaymentSplitter**: Create yield distribution splitter for grants
2. **Deploy Strategy**: Create ERC-4626 yield-skimming strategy
3. **Approve wstETH**: Allow Strategy to spend treasury wstETH
4. **Deposit wstETH**: Deposit wstETH, receiving shares to Treasury
## Architecture
The LidoStrategy IS the ERC-4626 vault (via Yearn's TokenizedStrategy).
No additional vault wrapper is needed since only one strategy is approved.
## Yield Distribution
- 100% → Dragon Funding Pool → Nouns Ecosystem Grants
## Risk Considerations
- Underlying: wstETH (Lido wrapped staked ETH)
- Exchange Rate Risk: ETH validator slashing could reduce rate (rare)
- Custody: Treasury retains full share ownership
- Liquidity: Instant withdrawals (no lockup period)
## Links
- [wstETH Token](https://etherscan.io/token/0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0)
- [Nouns Treasury](https://etherscan.io/address/0xb1a32FC9F9D8b2cf86C068Cae13108809547ef71)
- [PaymentSplitter Factory](https://etherscan.io/address/0x5711765E0756B45224fc1FdA1B41ab344682bBcb)
Prepared Calldata
Complete transaction calldata can be generated programmatically using the provided script:
forge script partners/nouns_dao/script/GenerateProposalCalldata.s.sol --fork-url $ETH_RPC_URL -vvvv
Before running, update the configuration in the script:
LIDO_STRATEGY_FACTORY— LidoStrategyFactory address (after deployment)TOKENIZED_STRATEGY— YieldSkimmingTokenizedStrategy address (after deployment)DRAGON_FUNDING_POOL— Actual grant recipient addressKEEPER_BOT— Dedicated keeper EOA/bot addressEMERGENCY_ADMIN— Emergency shutdown admin address (dedicated multisig/EOA, NOT Treasury)DEPOSIT_AMOUNT— Amount of wstETH to deposit
The script outputs:
- Precomputed CREATE2 address for Strategy
- Individual calldata for each transaction (TX 0-3)
- Formatted parameters for nouns.wtf proposal UI
Transaction 1: Deploy PaymentSplitter
Function: createPaymentSplitter(address[],string[],uint256[]) Selector: 0x7a0b30f3 Value: 0 Parameters: payees: [[GRANT_RECIPIENT_ADDRESS]] payeeNames: ["NounsGrants"] shares: [100]Transaction 2: Deploy Strategy
Target: [LIDO_STRATEGY_FACTORY_ADDRESS]
Function: createStrategy(string,address,address,address,address,bool,address)
Selector: 0x31d89943
Value: 0
Parameters:
_name: "NounsLidoStrategy"
_management: 0xb1a32FC9F9D8b2cf86C068Cae13108809547ef71
_keeper: [KEEPER_ADDRESS] (dedicated bot)
_emergencyAdmin: [EMERGENCY_ADMIN_ADDRESS]
_donationAddress: [PAYMENT_SPLITTER_ADDRESS]
_enableBurning: false
_tokenizedStrategyAddress: 0x8cf7246a74704bBE59c9dF614ccB5e3d9717d8Ac
Transaction 3: Approve wstETH
Target: 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0 (wstETH)
Function: approve(address,uint256)
Selector: 0x095ea7b3
Value: 0
Parameters:
spender: [STRATEGY_ADDRESS] (from Tx 2)
amount: [DEPOSIT_AMOUNT] (e.g., 1000000000000000000000 for 1000 wstETH)
Transaction 4: Deposit wstETH
Target: [STRATEGY_ADDRESS] (from Tx 2)
Function: deposit(uint256,address)
Selector: 0x6e553f65
Value: 0
Parameters:
assets: [DEPOSIT_AMOUNT] (e.g., 1000000000000000000000 for 1000 wstETH)
receiver: 0xb1a32FC9F9D8b2cf86C068Cae13108809547ef71 (Treasury)
Post-Deployment Operations
Harvest Operation
Report (Harvest Yield)
Target: [STRATEGY_ADDRESS]
Function: report()
Selector: 0x2606a10b
Value: 0
Parameters: (none)
Note: Called by management or keeper to harvest yield. Returns (uint256 profit, uint256 loss).
Mints profit shares to Dragon Router (PaymentSplitter).
Release Yield from PaymentSplitter
Step 1: Release strategy shares to payee
Target: [PAYMENT_SPLITTER_ADDRESS]
Function: release(address token, address account)
Selector: 0x48b75044
Value: 0
Parameters:
token: [STRATEGY_ADDRESS] (strategy shares)
account: [PAYEE_ADDRESS]
Note: Anyone can call this function. Transfers accumulated strategy shares to the payee.
Step 2: Payee redeems shares for wstETH
After receiving strategy shares, the payee must redeem them for underlying wstETH:
Target: [STRATEGY_ADDRESS]
Function: redeem(uint256 shares, address receiver, address owner)
Selector: 0xba087652
Value: 0
Parameters:
shares: Amount of strategy shares to redeem
receiver: Address to receive wstETH
owner: Payee's address (msg.sender)
Note: Only the payee (share owner) can redeem their own shares.
Important: The release() function only transfers strategy shares to the payee. The payee must then call redeem() on the strategy contract to convert those shares into actual wstETH.
Emergency Operations
Shutdown Strategy
Target: [STRATEGY_ADDRESS]
Function: shutdownStrategy()
Selector: 0xbe8f1668
Effect: Stops new deposits/mints, allows withdrawals.
Access: management or emergencyAdmin
Emergency Withdraw
Target: [STRATEGY_ADDRESS]
Function: emergencyWithdraw(uint256)
Selector: 0x5312ea8e
Parameters:
_amount: Amount to withdraw (up to full balance)
Prerequisite: Strategy must be shutdown first.
Access: management or emergencyAdmin
Standard Withdraw
Target: [STRATEGY_ADDRESS]
Function: withdraw(uint256,address,address)
Selector: 0xb460af94
Value: 0
Parameters:
assets: Amount of wstETH to withdraw
receiver: Address to receive wstETH
owner: 0xb1a32FC9F9D8b2cf86C068Cae13108809547ef71 (Treasury)
Note: Can be called anytime, no lockup period.
Quick Reference: Function Selectors
| Function | Selector | Target | Purpose |
|---|---|---|---|
createPaymentSplitter(...) | 0x7a0b30f3 | PaymentSplitter Factory | Deploy splitter |
createStrategy(...) | 0x31d89943 | LidoStrategy Factory | Deploy strategy |
approve(address,uint256) | 0x095ea7b3 | wstETH | Allow spending |
deposit(uint256,address) | 0x6e553f65 | Strategy | Deposit funds |
withdraw(uint256,address,address) | 0xb460af94 | Strategy | Withdraw funds |
report() | 0x2606a10b | Strategy | Harvest yield |
shutdownStrategy() | 0xbe8f1668 | Strategy | Emergency shutdown |
emergencyWithdraw(uint256) | 0x5312ea8e | Strategy | Emergency exit |
Operational Considerations
Keeper Setup
See Role Assignments for Keeper requirements. A dedicated EOA or bot enables autonomous harvesting without governance votes.
Recommended: Set up a keeper bot to call report() periodically (e.g., weekly or monthly) to:
- Update accounting
- Mint yield shares to Dragon Router
- Keep exchange rate tracking accurate
Emergency Admin
The Treasury serves as Emergency Admin. Emergency actions (shutdown, forced withdrawals) will follow standard DAO voting timelines (~7 days) unless a separate multisig is designated for faster response.
Withdrawals
The strategy provides instant liquidity (no lockup). Withdrawals are straightforward:
- Call
withdraw(assets, receiver, owner)orredeem(shares, receiver, owner)on the strategy - Receive underlying wstETH immediately
No unwinding of positions is needed — wstETH is held directly.
Value Debt Tracking
The strategy uses a dual-debt accounting system:
- User Debt: Tracks ETH value owed to depositors
- Dragon Debt: Tracks ETH value owed to Dragon Router (yield recipient)
This ensures:
- Users can always redeem their original value
- Dragon Router only receives actual profit
- Insolvency protection: Dragon operations blocked if vault can't cover user debt
Treasury Holdings (Reference)
Current Nouns DAO treasury holdings (as of snapshot):
| Asset | Amount | Value (USD) |
|---|---|---|
| wstETH | ~1,725 | ~$6.7M |
| rETH | ~163 | ~$600k |
| USDC | ~726k | ~$726k |
| ETH | ~517 | ~$1.6M |
Source: Etherscan Treasury